iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 7
0
自我挑戰組

從零開始的Flutter世界系列 第 7

Day07 Dart 語言介紹(五) 繼承、多型

  • 分享至 

  • xImage
  •  

繼承

  • 使用時機:多個class,有共同的屬性與規則

  • 耦合( Coupling ),內聚( Cohesion )

    耦合:相依的強度 → 每個class除了本身的屬性,還使用到別的class的屬性

    內聚:相關的強度 → 每個class本身才有的屬性,其他class沒有

  • 父類別( Super Class ),子類別( Sub Class ),繼承( extends )

    一個類別使用extends關鍵字來從另一個類別繼承,子類別就會繼承除了父類別的建構式之外的所有屬性和方法,其中一個子類別只能繼承一個父類別

    class Dog extends Animal {//子類別Dog 去繼承父類別Animal
      //...
    }
    
  • 建構式 Constructor

    1. 要用繼承,要先做好建構

    2. 執行順序:

      1. initializer list (初始化引數列表)

         class Point {
          num x;
          num y;
        
          Point(this.x, this.y);
        
          // 初始化列表在建構式scope 執行之前設定例項變數,x跟y變數,會經由建構式傳來的Map 資料去初始化
          Point.fromJson(Map jsonMap)
              : x = jsonMap['x'],
                y = jsonMap['y'] {
            print('In Point.fromJson(): ($x, $y)');
          }
        }
        
        main() {
          Point p = Point.fromJson({'x':4,'y':5}); //印出 In Point.fromJson(): (4, 5)
        }
        
      2. 父類別的建構式

      3. 子類別的建構式

    3. 需要透過建構式滿足繼承的規則

      • 當我們在新建子類別的物件時,如果其父類別的建構式有引數時,子類別需要通過建構式來滿足繼承的規則,子類別要繼承成功,一定要處理負責丟引數資料給父類別 → super(引數);,子類別透過在建構式執行super(引數); 把引數傳給父類別的建構,相當於是在 new 父類別(引數);

        若父類別有沒有引數的建構式,子類別的建構式可不用加super()

        只有在類別完全沒有設定任何建構式的時候,Dart 才會自動產生預設建構式( 沒有引數的建構式,其建構式裡也沒有定義要執行的動作 ),若之後有設定有引數的建構式了,但還是想要有一個沒引數的建構式,需要自己手動新增

        1. 父類別有無引數的建構式

          class Person {
            //Person無任何建構式,Dart 會自動預設,我們為了證明新增子類別物件時會去執行父類別的建構式,故在此新增一個無引數建構來印出我們預期的內容
            Person() {
              print('Person default constructor');
            }
          }
          
          class Employee extends Person {
            // Person 有預設建構式 (無引數建構)
            // 子類別Employee 的建構式可不用加super() 
            Employee(Map data) {
              print('Employee constructor $data');
            }
          }
          
          main() {
            Employee a = Employee({'x':4,'y':5});
            /*印出 Person default constructor
          	Employee constructor {x: 4, y: 5}
          	*/
          }
          
        2. 父類別沒有無引數的建構式

           class Person {
          
            Person(Map data) {
              print('Person $data');
            }
          }
          
          class Employee extends Person {
            // Person 沒有無引數建構式
            // 子類別Employee 的建構式需要滿足繼承的規則,一定要加super(data)
            Employee(Map data) :super(data){
              print('Employee $data');
            }
          }
          
          main() {
            Employee a = Employee({'x':4,'y':5});
            /*印出 Person {x: 4, y: 5}
          	Employee {x: 4, y: 5}
          	*/
          }
          
  1. 有多種建構時

    • 繼承的子類別只須要選一個建構來滿足繼承規則
      - 子類別的每個建構式只需super一次

      class Person {
        String _firstName;
      
        //主建構式為一個無引數建構式
        Person() {
          print('Person');
        }
      
        //命名建構式
        Person.fromString(this._firstName) {
          print('Person fromString $_firstName');
        }
      
        //命名建構式
        Person.fromJson(Map data) {
          print('Person fromJson $data');
        }
      }
      
      class Employee extends Person {
        // Person 有多種建構式,子類別Employee 的建構式只需選父類別的一個建構式來super,即能滿足繼承規則
      
        num x;
        num y;
      
        Employee(Map data) {
          //選無引數建構式,可不用加super()
          print('Employee $data');
        }
      
        Employee.fromString(String name) : super.fromString(name) {
          print('Employee fromString $name');
        }
      
        Employee.fromJson(Map data) : super.fromString(data['name']) {
          print('Employee fromJson $data');
        }
      
        Employee.fromJson2(Map data)
            : x = data['x'],
              y = data['y'],
              super.fromJson(data) {
          print('Employee fromJson2 $data, ($x, $y)');
        }
      }
      
      main() {
        Employee a = Employee({'x': 1, 'y': 2});
        Employee b = Employee.fromString('Ryder');
        Employee c = Employee.fromJson({'name': 'Ryder'});
        Employee d = Employee.fromJson2({'x': 5, 'y': 6});
      
        /*印出 
         Person
         Employee {x: 1, y: 2}
         Person fromString Ryder
         Employee fromString Ryder
         Person fromString Ryder
         Employee fromJson {name: Ryder}
         Person fromJson {x: 5, y: 6}
         Employee fromJson2 {x: 5, y: 6}, (5, 6)
       	*/
      }
      
  • Field 與Function 的處理

    1. Field:

      父類別Field若不是 private (變數名前面有加_),子類別可直接使用,若想使用或設定父類別private的屬性,需在父類別新增setter/getter的方法,通常除了常數都會加封裝private

    2. Function:

      • Override → 置換掉的意思,非修改

        當子類別有與父類別的方法,名稱一樣以及引數數量一樣,會強制要求遵守overrider規則,所以各引數型態也必須一樣,否則會編譯失敗

        欲override的父類別方法,不能用private 封裝設定方法

        super.方法應用 → 使用被override 掉的父類別方法

      class Person {
      
        void show(int data){
          print("Person $data");
        }
      }
      
      class Employee extends Person {
      
        String show(int data){
          super.show(data);
          return "Employee $data";
        }
      }
      
      main() {
        Employee a = Employee();
        var eShow = a.show(3); //印出 Person 3
        print(eShow); //印出 Employee 3
      } 
      

多型 Polymorphism

  1. 又稱異質宣告物件

  2. 目的:與List搭配使用,為了使用List去做分類的管理運用

  3. 語法:

    父類別 物件 = new 子類別( [ 引數值 ] ) ;

    new 在Dart 2後可以選擇不用加

  4. 會限制物件使用的方法數量,只有物件的宣告類別裡的方法才能使用

    • 編譯時,物件使用方法前會去宣告此物件的類別,檢查是否有此方法
    • 執行時,則以new的類別開始找
    • 多型new 出來的物件如果要用子類別的方法,需要在父類別新增一個同名稱同引數的方法,來給子類別override
    class Student {
      String _name;
    
      Student(this._name);
    
      String getMessage() {
        return "姓名 = $_name";
      }
    
      //尚未有show方法
    }
    
    class SocialGroupStudent extends Student {
      int _society;
    
      SocialGroupStudent(String name, this._society) : super(name);
    
      String getMessage() {
        return super.getMessage() + ", 社會 = $_society";
      }
    
      void show() {
        print(getMessage());
      }
    }
    
    class ScienceGroupStudent extends Student {
      int _science;
    
      ScienceGroupStudent(String name, this._science) : super(name);
    
      String getMessage() {
        return super.getMessage() + ", 自然 = $_science";
      }
    
      void show() {
        print(getMessage());
      }
    }
    
    main() {
      ScienceGroupStudent a = new ScienceGroupStudent("a", 95);
      SocialGroupStudent b = new SocialGroupStudent("b", 100);
    
      a.show(); //印出 姓名 = a, 自然 = 95
      b.show(); //印出 姓名 = b, 社會 = 100
    
      //多型
      Student c = new ScienceGroupStudent("c", 95);
    
      /*
      c.show();
      會報錯:Error: The method 'show' isn't defined for the class 'Student'.,因為物件c的reference為Student,編譯時在Student找不到show()方法
      所以想要使用 ScienceGroupStudent 的 show() 方法,需要在Student類別也新增一個show方法給    ScienceGroupStudent的show()來override
      */
    }
    

    故要修改為:

    class Student {
      String _name;
    
      Student(this._name);
    
      String getMessage() {
        return "姓名 = $_name";
      }
    
      void show() {
        /*
          是為了多型產生的物件能使用此方法才加,執行時會先從子類別開始,故在此的內容並不重要,目的為多型時給子類別override,若有物件為實體化Student的物件,要使用到此方法,也可設計內容
          */
      }
    }
    
    class SocialGroupStudent extends Student {
      int _society;
    
      SocialGroupStudent(String name, this._society) : super(name);
    
      String getMessage() {
        return super.getMessage() + ", 社會 = $_society";
      }
    
      void show() {
        print(getMessage());
      }
    }
    
    class ScienceGroupStudent extends Student {
      int _science;
    
      ScienceGroupStudent(String name, this._science) : super(name);
    
      String getMessage() {
        return super.getMessage() + ", 自然 = $_science";
      }
    
      void show() {
        print(getMessage());
      }
    }
    
    main() {
      //多型
      Student c = new ScienceGroupStudent("c", 95);
      Student d = new SocialGroupStudent("d", 100);
    
      c.show(); //印出 姓名 = c, 自然 = 95
      d.show(); //印出 姓名 = d, 社會 = 100
    }
    
  5. 物件轉型

    class Student {}
    
    class SocialGroupStudent extends Student {}
    
    class ScienceGroupStudent extends Student {}
    
    main() {
      Student student = new ScienceGroupStudent();
      print(student);
      //印出Instance of 'ScienceGroupStudent',為ScienceGroupStudent的例項,但宣告的類別為Student
    
      /*但是若直接
        ScienceGroupStudent scienceGroupStudent = student;
        會編譯失敗,當異質宣告時,宣告類別必須是實體化類別的父類別:
        父類別 物件 = new 子類別( [ 引數值 ] ) ;
        上面例子不符合規則
        */
    
      //當有繼承關係時,可以運用轉型 ( casting )
      ScienceGroupStudent scienceGroupStudent = student as ScienceGroupStudent;//降轉
      print(scienceGroupStudent);
      //印出Instance of 'ScienceGroupStudent',同student
    
      //student宣告類別為Student,與SocialGroupStudent也有繼承關係,也可以轉型,編譯時也可以成功,但是執行時會出現 Unhandled Exception: type 'ScienceGroupStudent' is not a subtype of type 'SocialGroupStudent' in type cast
      SocialGroupStudent socialGroupStudent = student as SocialGroupStudent;
      print(socialGroupStudent);
      //執行時相當於是在做
      //SocialGroupStudent socialGroupStudent = new ScienceGroupStudent();
      //故會報錯
    }
    

今天提到了關於繼承的觀念,這些觀念都會在之後開發專案時會有大量的實作,而多型的觀念,我們將會在下一篇有很多關於多型的運用,包括抽象的觀念、介面等等


上一篇
Day06 Dart 語言介紹(四) static、Exception
下一篇
Day08 Dart 語言介紹(六) 抽象類別、介面、混合類別
系列文
從零開始的Flutter世界30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言